package com.study.project.config.redis;

import java.util.*;

import com.study.project.utils.CommonUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.JedisSentinelPool;
import redis.clients.jedis.util.Pool;

public class RedisClient {
    private static final Logger LOGGER = LoggerFactory.getLogger(RedisClient.class);

    private int timeout = 5000;

    protected Pool<Jedis> jedisPool;

    public RedisClient(String host, Integer port, String password, int database) {
        this.jedisPool = new JedisPool(new JedisPoolConfig(), host, port, timeout, password, database);
        LOGGER.info("redis client created:{} {} {}", host, port, database);
    }

    public RedisClient(String host, Integer port, String password, JedisPoolConfig poolConfig, int database) {
        this.jedisPool = new JedisPool(poolConfig, host, port, timeout, password, database);
        LOGGER.info("redis client created:{} {} {}", host, port, database);
    }

    public RedisClient(String host, Integer port, String password, JedisPoolConfig poolConfig, String masterName,
        Set<String> sentinels, int database) {
        this.jedisPool = new JedisSentinelPool(masterName, sentinels, poolConfig, timeout, password, database);
        LOGGER.info("redis client created:{} {} {}", host, port, database);
    }

    public void destroy() {
        jedisPool.destroy();
        LOGGER.info("redis client destroyed.");
    }

    /**
     * @param key
     * @return
     * @see Jedis#exists(String)
     */
    public boolean exists(String key) {
        checkKey(key);
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();

            return jedis.exists(key);
        } finally {
            if (CommonUtils.notNull(jedis)) {
                jedis.close();
            }
        }
    }

    /**
     * @param key
     * @return
     */
    public Long incr(String key) {
        checkKey(key);
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            return jedis.incr(key);

        } finally {
            if (CommonUtils.notNull(jedis)) {
                jedis.close();
            }
        }
    }

    public Long expire(String key, int seconds) {
        checkKey(key);
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();

            return jedis.expire(key, seconds);
        } finally {
            if (CommonUtils.notNull(jedis)) {
                jedis.close();
            }
        }
    }

    public Long expireAt(String key, long unixTime) {
        checkKey(key);
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            Long result = jedis.expireAt(key, unixTime);

            return result;

        } finally {
            if (CommonUtils.notNull(jedis)) {
                jedis.close();
            }
        }
    }

    public String set(String key, String value) {
        checkKey(key);
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            String valueJson;
            valueJson = JSON.toJSONString(value, SerializerFeature.WriteClassName);
            return jedis.set(key, valueJson);
        } finally {
            if (CommonUtils.notNull(jedis)) {
                jedis.close();
            }
        }
    }

    public String setString(String key, String value) {
        checkKey(key);
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            return jedis.set(key, value);
        } finally {
            if (CommonUtils.notNull(jedis)) {
                jedis.close();
            }
        }
    }

    public String setEx(String key, int seconds, Object value) {
        checkKey(key);
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            String valueJson;
            valueJson = JSON.toJSONString(value);

            return jedis.setex(key, seconds, valueJson);
        } finally {
            if (CommonUtils.notNull(jedis)) {
                jedis.close();
            }
        }
    }

    public String setEx(String key, Object value) {
        checkKey(key);
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            String valueJson;
            valueJson = JSON.toJSONString(value);

            return jedis.set(key, valueJson);
        } finally {
            if (CommonUtils.notNull(jedis)) {
                jedis.close();
            }
        }
    }

    public String setExStr(String key, int seconds, String value) {
        checkKey(key);
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();

            return jedis.setex(key, seconds, value);
        } finally {
            if (CommonUtils.notNull(jedis)) {
                jedis.close();
            }
        }
    }

    public Object get(String key, Class clazz) {

        checkKey(key);
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            String valueJson = jedis.get(key);

            return JSON.parseObject(valueJson, clazz);

        } finally {
            if (CommonUtils.notNull(jedis)) {
                jedis.close();
            }
        }
    }

    public String getString(String key) {

        checkKey(key);
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            String value = jedis.get(key);

            return value;
        } finally {
            if (CommonUtils.notNull(jedis)) {
                jedis.close();
            }
        }
    }

    public Long del(String... keys) {
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();

            return jedis.del(keys);
        } finally {
            if (CommonUtils.notNull(jedis)) {
                jedis.close();
            }
        }
    }

    public Long del(String key) {
        checkKey(key);
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();

            return jedis.del(key);
        } finally {
            if (CommonUtils.notNull(jedis)) {
                jedis.close();
            }
        }
    }

    /****
     * map
     */

    public Long hset(String key, String field, String value) {
        checkKey(key);
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            Long result = jedis.hset(key, field, value);
            return result;

        } finally {
            if (CommonUtils.notNull(jedis)) {
                jedis.close();
            }
        }
    }

    public Long hincrby(String key, String field, long value) {
        checkKey(key);
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            Long result = jedis.hincrBy(key, field, value);
            return result;

        } finally {
            if (CommonUtils.notNull(jedis)) {
                jedis.close();
            }
        }
    }

    public Long hdel(String key, String... fields) {
        checkKey(key);
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            Long result = jedis.hdel(key, fields);
            return result;
        } finally {
            if (CommonUtils.notNull(jedis)) {
                jedis.close();
            }
        }
    }

    public String hget(String key, String field) {
        checkKey(key);
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            return jedis.hget(key, field);
        } finally {
            if (CommonUtils.notNull(jedis)) {
                jedis.close();
            }
        }
    }

    public <T> T hget(String key, String field, Class<T> tClass) {
        checkKey(key);
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            String value = jedis.hget(key, field);
            return JSON.parseObject(value, tClass);
        } finally {
            if (CommonUtils.notNull(jedis)) {
                jedis.close();
            }
        }
    }

    public <T> List<T> hgetForList(String key, String field, Class<T> tClass) {
        checkKey(key);
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            String value = jedis.hget(key, field);
            List<T> result = JSON.parseArray(value, tClass);
            if (result == null) {
                result = new ArrayList<>();
            }
            return result;
        } finally {
            if (CommonUtils.notNull(jedis)) {
                jedis.close();
            }
        }
    }

    public Map<String, String> hgetAll(String key) {
        checkKey(key);
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            Map<String, String> value = jedis.hgetAll(key);
            return value;

        } finally {
            if (CommonUtils.notNull(jedis)) {
                jedis.close();
            }
        }
    }

    public <T> Map<String, T> hgetAll(String key, Class<T> tClass) {
        Map<String, T> ret = new HashMap<String, T>();
        Map<String, String> all = hgetAll(key);
        for (String k : all.keySet()) {
            String v = all.get(k);
            ret.put(k, JSON.parseObject(v, tClass));
        }
        return ret;
    }

    /**
     * 查询该key的所有内容,并以 Map<String,List<T>> 的形式返回
     */
    public <T> Map<String, List<T>> hgetAllForHash(String key, Class<T> tClass) {
        Map<String, List<T>> ret = new HashMap<>();
        Map<String, String> all = hgetAll(key);
        for (String k : all.keySet()) {
            String v = all.get(k);
            ret.put(k, JSON.parseArray(v, tClass));
        }
        return ret;
    }

    public List<String> hgetAllValue(String key) {
        checkKey(key);
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            List<String> value = jedis.hvals(key);
            return value;

        } finally {
            if (CommonUtils.notNull(jedis)) {
                jedis.close();
            }
        }
    }

    public <T> List<T> hgetAllValue(String key, Class<T> tClass) {
        List<T> ret = new ArrayList<T>();
        List<String> values = hgetAllValue(key);
        for (String v : values) {
            ret.add(JSON.parseObject(v, tClass));
        }
        return ret;
    }

    /*
    使用此方法的条件: value 是 数组序列化后的字符串
     */
    public <T> List<T> hgetAllOfValueIsList(String key, Class<T> tClass) {
        List<T> ret = new ArrayList<T>();
        List<String> values = hgetAllValue(key);
        for (String v : values) {
            if (v == null) {
                continue;
            }
            ret.addAll(JSON.parseArray(v, tClass));
        }
        return ret;
    }

    public List<String> hmget(String key, List<String> fields) {
        checkKey(key);
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            List<String> value = jedis.hmget(key, fields.toArray(new String[fields.size()]));
            return value;

        } finally {
            if (CommonUtils.notNull(jedis)) {
                jedis.close();
            }
        }
    }

    /*
    使用此方法的条件: value 是 数组序列化后的字符串
     */
    public <T> List<T> hmget(String key, List<String> fields, Class<T> tClass) {
        List<String> values = hmget(key, fields);
        List<T> result = new ArrayList<>();
        for (String v : values) {
            if (v == null) {
                continue;
            }
            result.addAll(JSON.parseArray(v, tClass));
        }
        return result;
    }

    public String hmset(String key, Map<String, String> fieldMap) {
        checkKey(key);
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            return jedis.hmset(key, fieldMap);

        } finally {
            if (CommonUtils.notNull(jedis)) {
                jedis.close();
            }
        }
    }

    public boolean hexists(String key, String field) {
        checkKey(key);
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            return jedis.hexists(key, field);
        } finally {
            if (CommonUtils.notNull(jedis)) {
                jedis.close();
            }
        }
    }

    public Set<String> hkeys(String key) {
        checkKey(key);
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            return jedis.hkeys(key);
        } finally {
            if (CommonUtils.notNull(jedis)) {
                jedis.close();
            }
        }
    }

    public static String getKey(String baseKey, String... args) {
        return getRedisKey(baseKey, args);
    }

    public static String getRedisKey(String baseKey, String... args) {
        if (args == null || args.length == 0) {
            return baseKey;
        }
        int i = 0;
        for (String arg : args) {
            baseKey = baseKey.replace("{" + i++ + "}", arg);
        }
        return baseKey;
    }

    public Jedis getJedis() {
        try {
            if (jedisPool != null && !jedisPool.isClosed()) {
                return jedisPool.getResource();
            }
        } catch (Exception e) {
            LOGGER.error("get jedis from jedisPool error", e);
        }
        return null;
    }

    /****
     * @param key
     */
    private void checkKey(String key) {
        if (key == null || key.isEmpty()) {
            throw new IllegalArgumentException("Cache key is null or not a length of 0.");
        }
    }

}
